package ao.util.io;

import ao.util.data.Arr;
import ao.util.misc.Exceptions;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * see http://www.asciitable.com/
 *
 * AoFiles as opposed to Files to avoid name clash with google guava.
 *
 *
 * @author aostrovsky
 */
public class AoFiles
{
	//-------------------------------------------------------------------------
	private static final char[] escapeChars =
			"\\/:*?\"<>|".toCharArray();
	
	private static final char  allowFrom = ' ';
	private static final char  allowTill = '~';
	
	
	//-------------------------------------------------------------------------
	private AoFiles() {}
	
	
	//-------------------------------------------------------------------------
	public static void main(String[] args) {
		System.out.println(
				escapeName(
						"/\\,:?\">><<|||@!#D"));

        System.out.println(
                name("hello/world/foo.bar"));

        System.out.println(
                path("a/b/c.bar"));
        System.out.println(
                path("x\\y\\z.bar"));
	}
	

    //-------------------------------------------------------------------------
    public static File resource(
            Class<?> relativeTo,
            String   path)
    {
        URL url = relativeTo.getResource( path );
        
        try
        {
          return new File( url.toURI  () );
        }
        catch(URISyntaxException e)
        {
          return new File( url.getPath() );
        }
    }


    //-------------------------------------------------------------------------
    public static String name(String filePath) {
        return new File(filePath).getName();
    }

    public static String path(String ofFile)
    {
        int lastSlash = lastDirectorySeparator( ofFile );
        if (lastSlash == -1) {
//            return ofFile;
            return ".";
        } else {
            return ofFile.substring(0, lastSlash);
        }
    }

    private static int lastDirectorySeparator(String ofPath)
    {
        int lastDir = ofPath.lastIndexOf("/");
        return (lastDir == -1)
               ? ofPath.lastIndexOf("\\")
               : lastDir;
    }


	//-------------------------------------------------------------------------
	public static String escapeName(String fileName)
	{
		return escapeName(fileName, "_");
	}
	
	public static String escapeName(
			String fileName, String escape)
	{
		StringBuilder str = new StringBuilder();
		
		for (char c : fileName.toCharArray())
		{
			if (c < allowFrom ||
					c > allowTill ||
					Arr.indexOf(escapeChars, c) != -1)
			{
				str.append(escape);
			}
			else
			{
				str.append( c );
			}
		}
		
		return str.toString();
	}
	
	
	//-------------------------------------------------------------------------
	public static boolean copy(
			String in, String out) {
		return copy(new File(in), new File(out));
	}
	public static boolean copy(
			File in, String out) {
		return copy(in, new File(out));
	}
	public static boolean copy(
			String in, File out) {
		return copy(new File(in), out);
	}
	
	/**
	 * Compy contents of file file overwriting another.
	 * 
	 * Taken from
	 * 	http://www.rgagnon.com/javadetails/java-0064.html
	 * 
	 * In Java 7 there is a native mechanism for doing file copies,
	 * 	but I can't use that for now.
     * 
     * @param in source of copy
     * @param out destination of copy
     * @return true if and only if the file was successfully copied
     */
	public static boolean copy(
			File in, File out)
	{
		try {
			doCopy(in, out);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	private static void doCopy(
			File in, File out) throws Exception
	{
		InputStream  fis =
			new BufferedInputStream(
				new FileInputStream(in));
		
		OutputStream fos =
			new BufferedOutputStream(
				new FileOutputStream(out));
		
		try {
            int    i;
			byte[] buf = new byte[8 * 1024];
			while ((i = fis.read(buf)) != -1) {
				fos.write(buf, 0, i);
			}
		} finally {
			fis.close();
			fos.close();
		}
	}


    //-------------------------------------------------------------------------
    public static InputStream read(
            String inputFile)
    {
        return read(new File( inputFile ));
    }

    public static InputStream read(
            File inputFile)
    {
        try
        {
            return new BufferedInputStream(
                    new FileInputStream( inputFile ));
        }
        catch (FileNotFoundException e)
        {
            throw Exceptions.wrap( e );
        }
    }


    //-------------------------------------------------------------------------
    public static OutputStream write(String outFile)
    {
        return write( new File(outFile) );
    }

    public static OutputStream write(
            String outFile, boolean append)
    {
        return write( new File(outFile), append );
    }

    public static OutputStream write(File outFile)
    {
        return write(outFile, false);
    }

    public static OutputStream write(
            File outFile, boolean append)
    {
        try
        {
            return new BufferedOutputStream(
                    new FileOutputStream(
                            new File(Dirs.pathTo(outFile),
                                     outFile.getName()),
                            append));
        }
        catch (FileNotFoundException e)
        {
            throw Exceptions.wrap( e );
        }
    }


    //-------------------------------------------------------------------------
    public static FilterOutputStream newOuputFilter(
            Class<? extends FilterOutputStream> filter,
            OutputStream                        delegate)
    {
        try
        {
            return filter.getConstructor(
                     OutputStream.class
                   ).newInstance( delegate );
        }
        catch (Exception e)
        {
            throw Exceptions.wrap( e );
        }
    }

    public static FilterInputStream newInputFilter(
            Class<? extends FilterInputStream> filter,
            InputStream                        delegate)
    {
        try
        {
            return filter.getConstructor(
                     InputStream.class
                   ).newInstance( delegate );
        }
        catch (Exception e)
        {
            throw Exceptions.wrap( e );
        }
    }
}
